package com.mastermind.games;

import java.awt.*;
import java.awt.event.*;
import java.io.ObjectInputStream.GetField;
import java.util.*;

import javax.swing.*;

import com.mastermind.CONFIG;

public class BasicMastermind extends Mastermind {
	
	private JFrame container;
	
	public enum colors { BLACK, WHITE, RED, GREEN, BLUE, YELLOW, NONE };
	private int numColors = 6;
	
	private colors[][] board;
	private colors[] solution;
	
	private int boardWidth = 4, boardHeight = 10;
    private Random rand = new Random();

    private boolean anyPegSelected = false;
    private boolean pegPickedUp = false;
    private int lastPegOn = -1;
    private boolean[] inSelectionPeg;
    private Color pegPickedUpColor;
    
    private int currentRow = 7;
    private boolean overRowHole = false;
    private int lastRowHoleOn = -1;
    private boolean rowFilled = false;
    
    private int mx = 0, my = 0;
    
    private Font checkButtonFont;
	private Font mainFont;
	
	public BasicMastermind() {
		// initialize the board
		board = new colors[boardHeight][boardWidth];
		for (int y = 0; y < boardHeight; ++y) {
			for (int x = 0; x < boardWidth; ++x) {
				board[y][x] = colors.NONE;
			}
		}
		
		// initialize the solution
		solution = new colors[boardWidth];
		for (int x = 0; x < boardWidth; ++x) {
			solution[x] = colors.NONE;
		}
		
		// initialize the selected array
		inSelectionPeg = new boolean[numColors];
		for (int x = 0; x < numColors; ++x) {
			inSelectionPeg[x] = false;
		}
		
		// randomize the solution
		solution = generateSoultion();
	}
	
	private colors[] generateSoultion() {
		colors[] sol = new colors[boardWidth];
		for (int i = 0; i < boardWidth; i++) {
			int random = randInt(0, numColors - 1);
			switch (random) {
			case 0: {
				sol[i] = colors.BLACK;
				break;
			}
			case 1: {
				sol[i] = colors.WHITE;
				break;
			}
			case 2: {
				sol[i] = colors.RED;
				break;
			}
			case 3: {
				sol[i] = colors.GREEN;
				break;
			}
			case 4: {
				sol[i] = colors.BLUE;
				break;
			}
			case 5: {
				sol[i] = colors.YELLOW;
				break;
			}
			}
		}
		return sol;
	}

	public void drawBoard(Graphics2D g) {
		checkButtonFont = new Font(mainFont.getName(), mainFont.getStyle(), 24);
		
		// draw existing pieces on board
		int dx = 0, dy = 0;
		boolean onCurrentRow = false;
		rowFilled = true;
		for (int y = 0; y < boardHeight; ++y) {
			onCurrentRow = y == boardHeight - 1 - currentRow;
			for (int x = 0; x < boardWidth; ++x) {
				colors c = board[y][x];
				
				dx = (CONFIG.HOLE_SIZE * x) + (CONFIG.HOLE_X_OFFSET * (x + 1));
				dy = (CONFIG.HOLE_SIZE * y) + (CONFIG.HOLE_Y_OFFSET * (y + 1)) + CONFIG.TOP_OFFSET;
				
				switch (c) {
					case NONE: {
						if (y == boardHeight - 1 - currentRow) rowFilled = false;
						break;
					}
					case BLACK: {
						g.setColor(Color.BLACK);
						g.fillOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
						break;
					}
					case WHITE: {
						g.setColor(Color.WHITE);
						g.fillOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
						break;
					}
					case RED: {
						g.setColor(Color.RED);
						g.fillOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
						break;
					}
					case GREEN: {
						g.setColor(Color.GREEN);
						g.fillOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
						break;
					}
					case BLUE: {
						g.setColor(Color.BLUE);
						g.fillOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
						break;
					}
					case YELLOW: {
						g.setColor(Color.YELLOW);
						g.fillOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
						break;
					}
				default:
					break;
				}
				
				g.setColor(Color.BLACK);
				if (pegPickedUp && onCurrentRow && mouseInArea(dx, dy, CONFIG.HOLE_SIZE)) {
					g.setColor(Color.YELLOW);
					overRowHole =  true;
					lastRowHoleOn = x;
				}
				
				g.setStroke(new BasicStroke(CONFIG.HOLE_STROKE_SIZE));
				g.drawOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
			}
		}
		
		if (rowFilled) {
			// if the current row is filled, draw the 'check row' button
			g.setColor(Color.WHITE);
			g.setStroke(new BasicStroke(CONFIG.CURRENT_ROW_STROKE_SIZE));
			g.fillRect(CONFIG.CHECK_ROW_LEFT_OFFSET, CONFIG.WINDOW_SIZE.height -  (currentRow * CONFIG.CHECK_ROW_BOX_HEIGHT) - CONFIG.CHECK_ROW_BOTTOM_OFFSET - (CONFIG.CHECK_ROW_BOTTOM_RIG * currentRow), CONFIG.CHECK_ROW_BOX_WIDTH, CONFIG.CHECK_ROW_BOX_HEIGHT);
			g.setColor(Color.BLACK);
			g.drawRect(CONFIG.CHECK_ROW_LEFT_OFFSET, CONFIG.WINDOW_SIZE.height -  (currentRow * CONFIG.CHECK_ROW_BOX_HEIGHT) - CONFIG.CHECK_ROW_BOTTOM_OFFSET - (CONFIG.CHECK_ROW_BOTTOM_RIG * currentRow), CONFIG.CHECK_ROW_BOX_WIDTH, CONFIG.CHECK_ROW_BOX_HEIGHT);
		}
		
		// draw solution (or black box)
		if (CONFIG.SHOW_SOLUTION) {
			// draw solution
			for (int x = 0; x < boardWidth; ++x) {
				colors c = solution[x];
				
				dx = (CONFIG.HOLE_SIZE * x) + (CONFIG.HOLE_X_OFFSET * (x + 1));
				dy = CONFIG.SOLUTION_TOP_OFFSET;
				
				switch (c) {
					case BLACK: {
						g.setColor(Color.BLACK);
						break;
					}
					case WHITE: {
						g.setColor(Color.WHITE);
						break;
					}
					case RED: {
						g.setColor(Color.RED);
						break;
					}
					case GREEN: {
						g.setColor(Color.GREEN);
						break;
					}
					case BLUE: {
						g.setColor(Color.BLUE);
						break;
					}
					case YELLOW: {
						g.setColor(Color.YELLOW);
						break;
					}
				default:
					break;
				}
				g.fillOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
				
				g.setColor(Color.BLACK);
				g.setStroke(new BasicStroke(CONFIG.HOLE_STROKE_SIZE));
				g.drawOval(dx, dy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
			}
			
						
		} else {
			// black box
			g.setColor(Color.BLACK);
			g.fillRect(CONFIG.SOLUTION_MASK_LOCAION.x, CONFIG.SOLUTION_MASK_LOCAION.y, CONFIG.SOLUTION_MASK_SIZE.width, CONFIG.SOLUTION_MASK_SIZE.height);
		}
		
		// draw color selection area
		for (int x = 0; x < 6; ++x) {
			switch (x) {
				case 0: {
					g.setColor(Color.BLACK);
					break;
				}
				case 1: {
					g.setColor(Color.WHITE);
					break;
				}
				case 2: {
					g.setColor(Color.RED);
					break;
				}
				case 3: {
					g.setColor(Color.GREEN);
					break;
				}
				case 4: {
					g.setColor(Color.BLUE);
					break;
				}
				case 5: {
					g.setColor(Color.YELLOW);
					break;
				}
			}
			
			int ddx = (CONFIG.HOLE_SIZE * x) + (CONFIG.COLOR_SELECTION_HOLE_OFFSET * (x + 1));
			int ddy = CONFIG.COLOR_SELECTION_VERTICAL_OFFSET;

			g.fillOval(ddx, ddy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
			
			g.setColor(Color.BLACK);
			g.setStroke(new BasicStroke(CONFIG.HOLE_STROKE_SIZE));
			g.drawOval(ddx, ddy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
			
		}
		
		// draw black lines
		g.setColor(Color.BLACK);
		g.setStroke(new BasicStroke(5));
		g.drawLine(0, CONFIG.TOP_BAR_OFFSET, CONFIG.WINDOW_SIZE.width, CONFIG.TOP_BAR_OFFSET);
		g.drawLine(0, CONFIG.BOTTOM_BAR_OFFSET, CONFIG.WINDOW_SIZE.width, CONFIG.BOTTOM_BAR_OFFSET);
		g.drawLine(CONFIG.SIDE_BAR_OFFSET, 0, CONFIG.SIDE_BAR_OFFSET, CONFIG.SIDE_BAR_END);
		
		// draw current row highlight box
		g.setColor(Color.YELLOW);
		g.setStroke(new BasicStroke(CONFIG.CURRENT_ROW_STROKE_SIZE));
		g.drawRect(CONFIG.CURRENT_ROW_LEFT_OFFSET, CONFIG.WINDOW_SIZE.height -  (currentRow * CONFIG.CURRENT_ROW_BOX_HEIGHT) - CONFIG.CURRENT_ROW_BOTTOM_OFFSET - (CONFIG.CURRENT_ROW_BOTTOM_RIG * currentRow), CONFIG.CURRENT_ROW_BOX_WIDTH, CONFIG.CURRENT_ROW_BOX_HEIGHT);
		
		// crosshairs for mouse
		if (CONFIG.DRAW_MOUSE_CROSSHAIR) {
			g.setColor(Color.BLACK);
			g.setStroke(new BasicStroke(3));
			g.drawLine(0, my, CONFIG.WINDOW_SIZE.width, my);
			g.drawLine(mx, 0, mx, CONFIG.WINDOW_SIZE.height);
		}

		for (int x = 0; x < numColors; ++x) {
			inSelectionPeg[x] = false;
		}
		
		anyPegSelected = false;
		lastPegOn = -1;
		if (my > CONFIG.BOTTOM_BAR_OFFSET) {
			int loc = mx / (CONFIG.HOLE_SIZE + CONFIG.COLOR_SELECTION_HOLE_OFFSET);
			int locScaled = loc * (CONFIG.HOLE_SIZE + CONFIG.COLOR_SELECTION_HOLE_OFFSET);
			int zone = locScaled + CONFIG.COLOR_SELECTION_HOLE_OFFSET;
			
			if (!(mx >= locScaled && mx <= zone) && loc != 6) {
				inSelectionPeg[loc] = true;
				lastPegOn = loc;
				anyPegSelected = true;
			}
		}
		
		if (anyPegSelected) {
			container.setCursor(Cursor.HAND_CURSOR);
		} else {
			container.setCursor(Cursor.DEFAULT_CURSOR);			
		}
		//		for (int x = 0; x < numColors; ++x) {
		//			System.out.print(inSelectionPeg[x] + " ");
		//		}
		//		System.out.println();
		
		if (pegPickedUp) {
			g.setColor(pegPickedUpColor);
			int ddx = mx - CONFIG.HOLE_SIZE / 2; 
			int ddy = my - CONFIG.HOLE_SIZE / 2; 
			g.fillOval(ddx, ddy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
			g.setColor(Color.BLACK);
			g.setStroke(new BasicStroke(CONFIG.HOLE_STROKE_SIZE));
			g.drawOval(ddx, ddy, CONFIG.HOLE_SIZE, CONFIG.HOLE_SIZE);
		}
	}
	
	private boolean mouseInArea(int dx, int dy, int holeSize) {
		return ((mx >= dx && mx <= dx + holeSize) && (my >= dy && my <= dy + holeSize));
	}

	public int randInt(int min, int max) {

	    // NOTE: Usually this should be a field rather than a method
	    // variable so that it is not re-seeded every call.

	    // nextInt is normally exclusive of the top value,
	    // so add 1 to make it inclusive
	    int randomNum = rand.nextInt((max - min) + 1) + min;

	    return randomNum;
	}

	@Override
	public void click(MouseEvent e) {
		//System.out.println("Mouse button "+e.getButton()+" clicked at "+e.getX()+", "+e.getY());
		
		System.out.println(pegPickedUp + ", "+overRowHole);
		if (pegPickedUp && overRowHole && lastRowHoleOn != -1) {
			pegPickedUp = false;
			System.out.println("Dropped peg of "+pegPickedUpColor.toString()+" into hole "+ lastRowHoleOn);
			board[boardHeight - 1 - currentRow][lastRowHoleOn] = mapColorToEnum(pegPickedUpColor);
			overRowHole = false;
		}
		
		if (anyPegSelected && lastPegOn != -1) {
			// the mouse is over a peg - pick it up
			System.out.println("Clicked on peg "+lastPegOn);
			pegPickedUp = true;
			switch (lastPegOn) {
				case 0: {
					pegPickedUpColor = Color.BLACK;
					break;
				}
				case 1: {
					pegPickedUpColor = Color.WHITE;
					break;
				}
				case 2: {
					pegPickedUpColor = Color.RED;
					break;
				}
				case 3: {
					pegPickedUpColor = Color.GREEN;
					break;
				}
				case 4: {
					pegPickedUpColor = Color.BLUE;
					break;
				}
				case 5: {
					pegPickedUpColor = Color.YELLOW;
					break;
				}
			}
		} else {
			pegPickedUp = false;
		}
	}

	private colors mapColorToEnum(Color c) {
		if (c.equals(Color.BLACK)) return colors.BLACK;
		if (c.equals(Color.WHITE)) return colors.WHITE;
		if (c.equals(Color.RED)) return colors.RED;
		if (c.equals(Color.GREEN)) return colors.GREEN;
		if (c.equals(Color.BLUE)) return colors.BLUE;
		if (c.equals(Color.YELLOW)) return colors.YELLOW;
		return colors.NONE;
	}

	@Override
	public void mouse(MouseEvent e) {
		//System.out.println("Mouse movement: "+e.getX()+", "+e.getY());	
		this.mx = e.getX() - CONFIG.MOUSE_X_OFFSET;
		this.my = e.getY() - CONFIG.MOUSE_Y_OFFSET;
	}

	@Override
	public void setContainer(JFrame f) {
		this.container = f;
	}

	@Override
	public void setFont(Font font) {
		this.mainFont = font;		
	}
}
